var util = require('../topUtil.js')
var RestClient = require('./network.js')
var Stream = require('stream')

/**
 * TOP API Client.
 *
 * @param {Object} options, must set `appkey` and `appsecret`.
 * @constructor
 */

function TopClient(options) {
    if (!(this instanceof TopClient)) {
        return new TopClient(options)
    }
    options = options || {}
    if (!options.appkey || !options.appsecret) {
        throw new Error('appkey or appsecret need!')
    }
    this.url = options.url || 'http://gw.api.taobao.com/router/rest'
    this.appkey = options.appkey
    this.appsecret = options.appsecret
}

/**
 * Invoke an api by method name.
 *
 * @param {String} method, method name
 * @param {Object} params
 * @param {Array} reponseNames, e.g. ['tmall_selected_items_search_response', 'tem_list', 'selected_item']
 * @param {Object} defaultResponse
 * @param {Function(err, response)} callback
 */
TopClient.prototype.invoke = function (type, method, params, reponseNames, httpHeaders, callback) {
    params.method = method
    this.request(type, params, httpHeaders, function (err, result) {
        if (err) {
            return callback(err)
        }
        var response = result
        if (reponseNames && reponseNames.length > 0) {
            for (var i = 0; i < reponseNames.length; i++) {
                var name = reponseNames[i]
                response = response[name]
                if (response === undefined) {
                    break
                }
            }
        }
        callback(null, response)
    })
}

/**
 * Request API.
 *
 * @param {Object} params
 * @param {String} [type='GET']
 * @param {Function(err, result)} callback
 * @public
 */
TopClient.prototype.request = function (type, params, httpHeaders, callback) {
    var err = util.checkRequired(params, 'method')
    if (err) {
        return callback(err)
    }
    var args = {
        timestamp: this.timestamp(),
        format: 'json',
        app_key: this.appkey,
        v: '2.0',
        sign_method: 'md5'
    }

    var request = null
    if (type == 'get') {
        request = RestClient.get(this.url)
    } else {
        request = RestClient.post(this.url)
    }

    for (var key in params) {
        if (typeof params[key] === 'object' && Buffer.isBuffer(params[key])) {
            request.attach(key, params[key], { knownLength: params[key].length, filename: key })
        } else if (typeof params[key] === 'object' && Stream.Readable(params[key]) && !util.is(params[key]).a(String)) {
            request.attach(key, params[key])
        } else if (typeof params[key] === 'object') {
            args[key] = JSON.stringify(params[key])
        } else {
            args[key] = params[key]
        }
    }

    args.sign = this.sign(args)

    for (var key in httpHeaders) {
        request.header(key, httpHeaders[key])
    }

    for (var key in args) {
        request.field(key, args[key])
    }

    request.end(function (response) {
        if (response.statusCode == 200) {
            var data = response.body
            var errRes = data && data.error_response
            if (errRes) {
                callback(errRes, data)
            } else {
                callback(err, data)
            }
        } else {
            err = new Error('NetWork-Error')
            err.name = 'NetWork-Error'
            err.code = 15
            err.sub_code = response.statusCode
            callback(err, null)
        }
    })
}

/**
 * Get now timestamp with 'yyyy-MM-dd HH:mm:ss' format.
 * @return {String}
 */
TopClient.prototype.timestamp = function () {
    return util.YYYYMMDDHHmmss()
}

/**
 * Sign API request.
 * see http://open.taobao.com/doc/detail.htm?id=111#s6
 *
 * @param  {Object} params
 * @return {String} sign string
 */
TopClient.prototype.sign = function (params) {
    var sorted = Object.keys(params).sort()
    var basestring = this.appsecret
    for (var i = 0, l = sorted.length; i < l; i++) {
        var k = sorted[i]
        basestring += k + params[k]
    }
    basestring += this.appsecret
    return util.md5(basestring).toUpperCase()
}

/**
 * execute top api
 */
TopClient.prototype.execute = function (apiname, params, callback) {
    this.invoke('post', apiname, params, [util.getApiResponseName(apiname)], [], callback)
}

TopClient.prototype.executeWithHeader = function (apiname, params, httpHeaders, callback) {
    this.invoke('post', apiname, params, [util.getApiResponseName(apiname)], httpHeaders || [], callback)
}

TopClient.prototype.get = function (apiname, params, callback) {
    this.invoke('get', apiname, params, [util.getApiResponseName(apiname)], [], callback)
}

exports.TopClient = TopClient
